﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace DynamicProperties.Tests
{
	[TestClass]
	public class DefaulValue_ReferenceTypeTests
	{
		class MyType { }
	
		DynamicProperty<object, MyType> testReferencePropertyNoDefault = new DynamicProperty<object, MyType>("Reference, No default");
		DynamicProperty<object, MyType> testReferencePropertyDefaultDirect = new DynamicProperty<object, MyType>("Reference, Default direct", new MyType()/*default*/);
		DynamicProperty<object, MyType> testReferencePropertyDefaultFunc = new DynamicProperty<object, MyType>("Reference, Default func", () => new MyType()/*default*/);

		[TestMethod]
		public void DefaultReferenceTypeNoDefault()
		{
			// check if default value is correctly null (if reference type has no default value)
			var obj1 = new object();
			Assert.IsNull(testReferencePropertyNoDefault.Get(obj1));
		}

		[TestMethod]
		public void DefaultReferenceTypeDefaultDirect()
		{
			// check if default values are correct (default value is "singleton")
			var obj1 = new object();
			var val1_1 = testReferencePropertyDefaultDirect.Get(obj1); // new instance is created
			var val1_2 = testReferencePropertyDefaultDirect.Get(obj1); // same instance is used as in val1_1

			var obj2 = new object();
			var val2_1 = testReferencePropertyDefaultDirect.Get(obj2); // new instance is created
			var val2_2 = testReferencePropertyDefaultDirect.Get(obj2); // same instance is used as in val2_1

			// no null is expected
			Assert.IsNotNull(val1_1);
			Assert.IsNotNull(val1_2);
			Assert.IsNotNull(val2_1);
			Assert.IsNotNull(val2_2);

			// after first call "get" default value should be created so second call returns same instance
			Assert.AreSame(val1_1, val1_2);
			Assert.AreSame(val2_1, val2_2);

			// every object instance (obj1 and obj2) should have same default property value
			Assert.AreSame(val1_1, val2_1);
		}

		[TestMethod]
		public void DefaultReferenceTypeDefaultFunc()
		{
			// check if default values are correct (default value is created by delegate so it should be unique for every object)
			var obj1 = new object();
			var val1_1 = testReferencePropertyDefaultFunc.Get(obj1); // new instance of default value is created
			var val1_2 = testReferencePropertyDefaultFunc.Get(obj1); // same instance is used (because default value for this object was created before)
			
			var obj2 = new object();
			var val2_1 = testReferencePropertyDefaultFunc.Get(obj2); // new instance of default value is created
			var val2_2 = testReferencePropertyDefaultFunc.Get(obj2); // same instance is used (because default value for this object was created before)

			// no null is expected
			Assert.IsNotNull(val1_1);
			Assert.IsNotNull(val1_2);
			Assert.IsNotNull(val2_1);
			Assert.IsNotNull(val2_2);

			// after first call "get" default value should be created so second call returns same instance
			Assert.AreSame(val1_1, val1_2);
			Assert.AreSame(val2_1, val2_2);

			// both instances (obj1 and obj2) should have its own instance created
			Assert.AreNotSame(val1_1, val2_1);
		}

		[TestMethod]
		public void DefaultReferenceTypeNoDefaultWithReset()
		{
			// check if value is set back to default value after reset
			var obj1 = new object();

			// set value for now
			var beforeResetValue = new MyType();
			testReferencePropertyNoDefault.Set(obj1, beforeResetValue);

			// and reset object
			Extender.Global.ResetObject(obj1);

			// read value after reset
			Assert.IsNull(testReferencePropertyNoDefault.Get(obj1));
		}

		[TestMethod]
		public void DefaultReferenceTypeDefaultDirectWithReset()
		{
			// check if value is set back to default value after reset
			var obj1 = new object();

			// read first default value (should be singleton default instance - defined as parameter in property definition)
			var val1 = testReferencePropertyDefaultDirect.Get(obj1);

			// change property value to new instance
			var val2 = new MyType();
			testReferencePropertyDefaultDirect.Set(obj1, val2);
			Assert.AreSame(val2, testReferencePropertyDefaultDirect.Get(obj1));

			// and reset object
			Extender.Global.ResetObject(obj1);

			// read value after reset (should be same as val1 instance because default value is singleton)
			MyType val3 = testReferencePropertyDefaultDirect.Get(obj1);

			// null value is not expected
			Assert.IsNotNull(val1);
			Assert.IsNotNull(val2);
			Assert.IsNotNull(val3);
	
			// check values
			Assert.AreSame(val1, val3); // both default values should be same
			Assert.AreNotSame(val2, val3); // new instance and default value after reset should be different
		}

		[TestMethod]
		public void DefaultReferenceTypeDefaultFuncWithReset()
		{
			// check if value is set back to default value after reset
			var obj1 = new object();

			// read first default value (should create new instance - default value is defined by delegate)
			var val1 = testReferencePropertyDefaultDirect.Get(obj1);

			// change property value to new instance
			var val2 = new MyType();
			testReferencePropertyDefaultDirect.Set(obj1, val2);
			Assert.AreSame(val2, testReferencePropertyDefaultDirect.Get(obj1));

			// and reset object
			Extender.Global.ResetObject(obj1);

			// read value after reset (should be new instance because default value in defined by delegate)
			MyType val3 = testReferencePropertyDefaultFunc.Get(obj1);

			// null value is not expected
			Assert.IsNotNull(val1);
			Assert.IsNotNull(val2);
			Assert.IsNotNull(val3);

			// check values
			Assert.AreNotSame(val1, val3); // default values should be different (each time created by delegate)
			Assert.AreNotSame(val2, val3); // new instance and default value after reset should be different
		}
	}
}
